package com.mazaiting.ftp;

import com.mazaiting.ftp.constant.FtpConstant;
import com.mazaiting.ftp.interfaces.IStreamHolder;
import com.mazaiting.ftp.domain.entity.FileEntity;
import com.mazaiting.ftp.domain.entity.FtpConfig;
import com.mazaiting.ftp.exception.FtpException;
import com.mazaiting.ftp.utils.FtpUtil;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.List;
import java.util.Objects;

/**
 * FTP 管理工具
 *
 * @author mazaiting
 * @create_time 2022/4/21 15:59
 */
public class FtpManager {

    /**
     * 日志工具
     */
    private final Logger logger = LoggerFactory.getLogger(FtpManager.class);

    /**
     * Ftp 管理者
     */
    private final FtpUtil ftpUtil;

    /**
     * Ftp配置对象
     */
    private final FtpConfig ftpConfig;

    public FtpManager(FtpConfig ftpConfig) {
        this.ftpConfig = ftpConfig;
        this.ftpUtil = new FtpUtil(ftpConfig);
    }

    /**
     * 单例
     *
     * @param ftpConfig ftp 配置
     * @return 管理者
     */
    public static FtpManager getInstance(FtpConfig ftpConfig) {
        // 返回Ftp管理对象
        return new FtpManager(ftpConfig);
    }

    /**
     * 单文件上传
     *
     * @param fileEntity 文件上传对象实体
     */
    public void upload(FileEntity fileEntity) {
        upload(fileEntity, false);
    }

    /**
     * 上传文件
     *
     * @param fileEntity 文件上传对象实体
     * @param isActive   是否是最后一个文件上传
     */
    public void upload(FileEntity fileEntity, boolean isActive) {
        upload(fileEntity, () -> {
            // 文件创建
            File file = new File(fileEntity.getLocalDir(), fileEntity.getLocalFileName());
            // 判断该路径是否存在
            if (!file.exists()) {
                throw new FtpException(FtpConstant.UPLOAD_FILE_NULL);
            }
            // 返回文件流对象
            return new FileInputStream(file);
        }, isActive);
    }

    /**
     * 使用文件流进行上传
     *
     * @param fileEntity  文件上传对象实体
     * @param inputStream 文件流
     */
    public void upload(FileEntity fileEntity, InputStream inputStream) {
        upload(fileEntity, () -> inputStream, false);
    }

    /**
     * 多文件流上传
     *
     * @param fileEntity  文件上传对象实体
     * @param inputStream 文件流
     * @param isActive    是否是最后一个上传的文件
     */
    public void upload(FileEntity fileEntity, InputStream inputStream, boolean isActive) {
        upload(fileEntity, () -> inputStream, isActive);
    }

    /**
     * 单文件上传
     *
     * @param fileEntity 文件实体
     */
    private void upload(FileEntity fileEntity, IStreamHolder holder, boolean isActive) {
        //1 测试连接 判断是否是正在连接
        if (!ftpUtil.isConnected()) {
            // 非正在连接则进行连接
            ftpUtil.connect();
        }
        // 2.创建目录
        if (!ftpUtil.mkdirDirectory(ftpUtil.getRemoteDir(ftpConfig.getRootPath(), fileEntity.getRemoteDir()))) {
            ftpUtil.disconnect();
            return;
        }
        // 获取远程文件名
        String remoteFileName = fileEntity.getRemoteFileName();
        // 远程文件名为空
        if (Objects.isNull(remoteFileName) || remoteFileName.isEmpty()) {
            // 则使用本地文件名代替
            remoteFileName = fileEntity.getLocalFileName();
            if (Objects.isNull(remoteFileName) || remoteFileName.isEmpty()) {
                throw new FtpException(FtpConstant.UPLOAD_FILE_NAME_NULL);
            }
            // 3 检查是否上传成功
            try (InputStream is = holder.getInputStream()) {
                if (ftpUtil.storeFile(remoteFileName, is)) {
                    // todo 可以记录是否上传成功
                } else {
                    // todo 可以记录是否上传成功
                    logger.error("文件:{},上传失败", remoteFileName);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 是否是多文件上传，是则不关闭Ftp，连接 否则关闭
            if (!isActive) {
                // 关闭Ftp 连接
                ftpUtil.disconnect();
            }
        }
    }
    /**
     * 多文件上传
     *
     * @param list 文件列表
     */
    public void upload(List<FileEntity> list) {
        // 多文件上传遍历
        for (int i = 0, length = list.size(); i < length; i++) {
            // 获取上传文件实体
            FileEntity fileEntity = list.get(i);
            upload(fileEntity, i != length - 1);
        }
    }



    /**
     * 文件下载
     * @param fileEntity   文件实体
     * @param isActive 是否是最后一个文件
     */
    public void download(FileEntity fileEntity,  boolean isActive) {
        // 是否链接成功
        //1 测试连接 判断是否是正在连接
        if (!ftpUtil.isConnected()) {
            // 非正在连接则进行连接
            ftpUtil.connect();
        }
        // 获取远程路径
        String remoteDir = fileEntity.getRemoteDir();
        // 获取要下载的文件名称
        String remoteFileName = fileEntity.getRemoteFileName();
        // 要下载到哪个本地路径
        String localDir = fileEntity.getLocalDir();
        // 下载之后得名字
        String localFileName = fileEntity.getLocalFileName();

        if(Objects.isNull(localFileName) || "".equals(localFileName)){
            localFileName = remoteFileName;
        }
        // 创建文件
        File file = new File(localDir, localFileName);
        // 开始寻找文件
        try (FileOutputStream fos = new FileOutputStream(file)) {
            // 要下载的目录是否存在
            if (ftpUtil.changeWorkingDirectory(remoteDir)) {
                // 获取该目录下所有文件
                FTPFile[] ftpFiles = ftpUtil.listFiles();
                // 遍历所有文件
                for (FTPFile ftpFile : ftpFiles) {
                    // 判断是否是你需要的文件
                    if (ftpFile.getName().equals(remoteFileName)) {
                        ftpUtil.retrieveFile(remoteFileName, fos);
                        break;
                    }else{
                        logger.error("文件未找到");
                    }
                }
            }else{
                logger.error("服务器路径不存在-");
            }
        } catch (IOException e) {
            logger.error("文件路径不存在");
            e.printStackTrace();
        }
        // 是否是多文件上传，是则不关闭Ftp，连接 否则关闭
        if(!isActive){
            // 关闭Ftp 连接
            ftpUtil.disconnect();
        }
    }

    /**
     * 多文件下载
     *
     * @param list 下载文件列表
     */
    public void download(List<FileEntity> list) {
        // 多文件上传遍历
        for (int i = 0, length =  list.size(); i < length; i++) {
            // 获取上传文件实体
            FileEntity fileEntity = list.get(i);
            download(fileEntity, i != length - 1);
        }
    }

}
